package com.cl.code.generate.module.java.generator;

import com.cl.code.generate.common.strategy.TableBaseGenerationStrategy;
import com.cl.code.generate.module.java.strategy.JavaFileGenerationStrategy;
import com.cl.code.generate.source.database.DatabaseConnector;
import com.cl.code.generate.source.database.entity.TableMetadata;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * 代码生成器，负责根据模板生成代码文件
 *
 * @author chengliang
 * @since 1.0.0
 */
public class JavaFileGenerator {
    private final DatabaseConnector connector;
    private final String outputDir;
    private boolean overwriteExisting = false;
    private String basePackagePath = "/templates";
    private static final String FILE_NAME = "fileName";
    private static final String PACKAGE_CACHE = "packageCache";
    private final Map<String, String> packageCache = new HashMap<>(10);

    public static JavaFileGenerator getInstance(DatabaseConnector connector, String outputDir) {
        return new JavaFileGenerator(connector, outputDir);
    }

    /**
     * 创建代码生成器
     *
     * @param connector 数据库连接器
     * @param outputDir 输出目录
     */
    private JavaFileGenerator(DatabaseConnector connector, String outputDir) {
        this.connector = connector;

        this.outputDir = outputDir;
    }


    public JavaFileGenerator overwriteExisting(boolean overwrite) {
        this.overwriteExisting = overwrite;
        return this;
    }

    public JavaFileGenerator basePackagePath(String basePackagePath) {
        this.basePackagePath = basePackagePath;
        return this;
    }

    /**
     * 生成代码文件
     *
     * @param strategy 代码生成策略
     */
    public void generate(TableBaseGenerationStrategy strategy) throws Exception {
        // 获取表元数据
        TableMetadata tableMetadata = connector.getTableMetadata(strategy.getTableName());
        // 构建数据模型
        Map<String, Object> dataModel = strategy.buildDataModel(tableMetadata);

        // 添加包缓存
        if (!packageCache.isEmpty()) {
            dataModel.put(PACKAGE_CACHE, packageCache);
        }

        Configuration templateConfig = new Configuration(Configuration.VERSION_2_3_32);
        templateConfig.setDefaultEncoding(StandardCharsets.UTF_8.name());

        // 加载模板并生成代码
        String templatePath = strategy.getTemplatePath();
        int lastSlash = templatePath.lastIndexOf(File.separator);
        if (lastSlash > 0) {
            String basePath = templatePath.substring(0, lastSlash);
            if (!basePath.startsWith(File.separator)) {
                basePath = File.separator + basePath;
            }
            templateConfig.setClassForTemplateLoading(this.getClass(), basePath);
            templatePath = templatePath.substring(lastSlash + 1);
        } else {
            templateConfig.setClassForTemplateLoading(this.getClass(), basePackagePath);
        }

        String code = processTemplate(templateConfig, templatePath, dataModel);

        // 输出生成的文件
        String fullOutputDir = outputDir + File.separator + strategy.getOutputPath();
        File dir = new File(fullOutputDir);
        if (!dir.exists() && !dir.mkdirs()) {
            throw new IOException("Failed to create directory: " + fullOutputDir);
        }
        // 判断必须包含 fileName 字段
        if (!dataModel.containsKey(FILE_NAME)) {
            throw new IllegalArgumentException("数据模型中必须包含 'fileName' 字段");
        }
        String fileName = (String) dataModel.get(FILE_NAME);
        String filePath = String.format("%s" + File.separator + "%s%s", fullOutputDir, fileName, strategy.getFileExtension());
        File file = new File(filePath);
        if (file.exists() && !overwriteExisting) {
            System.out.println("文件已存在，放弃生成: " + filePath);
            return;
        }
        try (FileWriter writer = new FileWriter(filePath)) {
            writer.write(code);
            System.out.println(file.exists() ? "已覆盖文件: " + filePath : "已生成文件: " + filePath);
        }
        // 如果是Java类 则缓存其包路径
        if (strategy instanceof JavaFileGenerationStrategy javaStrategy) {
            String packagePath = javaStrategy.getPackageName();
            if (packagePath != null && !packagePath.isEmpty()) {
                packageCache.put(fileName, packagePath + "." + fileName);
            }
        }
    }

    /**
     * 处理模板并生成代码
     *
     * @param templateConfig 模板配置
     * @param templateName   模板名称
     * @param dataModel      数据模型
     * @return 生成的代码内容
     */
    private String processTemplate(Configuration templateConfig, String templateName, Object dataModel) throws IOException, TemplateException {
        Template template = templateConfig.getTemplate(templateName);
        StringWriter writer = new StringWriter();
        template.process(dataModel, writer);
        return writer.toString();
    }
}